package v9.assist.analyst

import android.graphics.Bitmap
import sample.ui.SmallMapLayout
import abc.reflect.App
import kotlinx.coroutines.script.BLACKSMITH
import kotlinx.coroutines.script.BLUE_DOOR
import kotlinx.coroutines.script.BORDER
import kotlinx.coroutines.script.Block
import kotlinx.coroutines.script.C
import kotlinx.coroutines.script.ENEMY
import kotlinx.coroutines.script.EQUIP
import kotlinx.coroutines.script.GComponents
import kotlinx.coroutines.script.NONE
import kotlinx.coroutines.script.NPC
import kotlinx.coroutines.script.PLAN
import kotlinx.coroutines.script.PathNodeAStar
import kotlinx.coroutines.script.RED_DOOR
import kotlinx.coroutines.script.RED_DOT
import kotlinx.coroutines.script.ROAD
import kotlinx.coroutines.script.SELF
import kotlinx.coroutines.script.analysis.Analysis
import kotlinx.coroutines.script.averageMoveAngle
import kotlinx.coroutines.script.SMapBase
import kotlinx.coroutines.script.SMapBase.hitBlacksmith
import kotlinx.coroutines.script.SMapBase.hitBlacksmithBorder
import kotlinx.coroutines.script.SMapBase.hitBlueDoor
import kotlinx.coroutines.script.SMapBase.hitEquipO
import kotlinx.coroutines.script.SMapBase.hitRedDoor
import kotlinx.coroutines.script.SMapBase.hitSMapEnemyBlue
import kotlinx.coroutines.script.SMapBase.hitSMapEnemyOrange
import kotlinx.coroutines.script.SMapBase.hitSMapEnemyPurple
import kotlinx.coroutines.script.SMapBase.hitSMapEnemyPurpleExclude
import kotlinx.coroutines.script.SMapBase.hitSMapEnemyRedDot
import kotlinx.coroutines.script.SMapBase.hitSMapEnemyYellow
import kotlinx.coroutines.script.SMapBase.hitSMapNpc
import kotlinx.coroutines.script.SMapBase.hitSelf
import kotlinx.coroutines.script.setupElementsByBlock
import kotlinx.coroutines.script.step
import kotlinx.coroutines.script.takeTargetAStarPath
import kotlinx.coroutines.script.tes.ColorDiffer
import kotlinx.coroutines.script.tes.containsSimilarColor
import kotlinx.coroutines.script.tes.isDebug
import kotlinx.coroutines.script.tes.printLogE
import kotlinx.coroutines.script.tes.pt
import kotlinx.coroutines.script.tes.random
import java.util.LinkedList
import kotlin.math.max
import kotlin.math.sqrt

val smallMap get() = GComponents.get().smallMap
val selfInSmallMap get() = GComponents.get().selfInSmallMap
val sp = SMapBase.sp

private var runningSceneName: String? = null
fun setupAnalystScene(name: String) { runningSceneName = name }
private var lastBfsPath:  PathNodeAStar? = null
var lastBlueDoorTs = 0L
var lastMapDotBlueDoorTs = 0L
var lastBlacksmithTs = 0L
var lastEliteEnemyTs = 0L
var lastEliteEnemyBossTs = 0L
var lastSMapTargetToBossTs = 0L
var lastSMApTargetToEliteEnemyTs = 0L
var lastSMapTargetToNpcTs = 0L
var lastSMapTargetToNpcDs = 0

var blockBlueDoor: Block? = null
var blockBlacksmith: Block? = null
var blockEliteEnemy: Block? = null
var blockEliteEnemyBoss: Block? = null
var angleByRoad: Int? = null
var lastAtkRandomAngle = random.nextInt(360)
var stepAtkRandom = 0L

fun analysisMap(ss: Bitmap?) {
    if (ss == null) return
    blockBlueDoor = null
    blockBlacksmith = null
    blockEliteEnemy = null
    blockEliteEnemyBoss = null
    val startTs = System.currentTimeMillis()
    when (runningSceneName) {
        C.S_JT -> analysisMapJT(ss)
        C.S_HM -> analysisMapHM(ss)
        C.S_FW -> analysisMapFW(ss)
        C.S_HY -> analysisMapHY(ss)
        C.S_JL -> analysisMapJL(ss)
        C.S_SM -> analysisMapSM(ss)
        C.S_FB -> analysisMapFB(ss)
        C.S_YW -> analysisMapYW(ss)
        C.S_RH -> analysisMapRH(ss)
        C.S_FM -> analysisMapFM(ss)
    }
    printLogE { "analysisMap cost ${System.currentTimeMillis() - startTs}ms" }
    SmallMapLayout.setDisplayPoint(bsPage == PAGE_MAIN)
}

fun analysisMapElements(ss: Bitmap,
                        baseAnalysisBlueDoor: Boolean = true,
                        foreach: (x: Int, y: Int)-> Unit = { _, _ -> }) {
    var x = max(0, smallMap.right - 1)
    while (x > smallMap.left) { // 从右往左扫描. 从上往下扫描
        var y = smallMap.top
        while (y < smallMap.bottom) {
            val color = App.take().takePixel(ss, x, y)
            if (baseAnalysisBlueDoor && blockBlueDoor == null && hitBlueDoor.containsSimilarColor(color)) {
                val check = Block("小地图传送门", x , y, 16.pt, 16.pt) // 0.7654321 hitCount 62 total 81
                if (Analysis.hit(ss, check, hitBlueDoor, 3.pt, 0.18f, )) {  // Boss这里的门容易误判
                    blockBlueDoor = check
                    lastBlueDoorTs = System.currentTimeMillis()
                }
            } else if (blockBlacksmith == null && hitBlacksmithBorder.containsSimilarColor(color)) {
                val block = Block("铁匠图标", x , y, 20.pt, 20.pt)
                // hit [铁匠图标] 0.7755102 hitCount 38 total 49
                // hit [铁匠图标] 0.46938777 hitCount 23 total 49
                if (Analysis.hit(ss, block, hitBlacksmith, 3.pt, 0.22f)) { // 这个副本容易误判
                    blockBlacksmith = block
                    lastBlacksmithTs = System.currentTimeMillis()
                }
            } else if (blockEliteEnemyBoss == null && hitSMapEnemyPurple.containsSimilarColor(color)) {
                val data = Block("enemySMapP", x, y, 10.pt, 16.pt)
                // 如果中间有白色就是组队标记 // 不能说读到任意一个颜色就算，hitCount 1 total 60
                if (Analysis.hit(ss, data, hitSMapEnemyPurple, 3.pt, 0.25f)
                    && !Analysis.hitAny(ss, data, hitSMapEnemyPurpleExclude, 2.pt)) {
                    blockEliteEnemyBoss = data
                    lastEliteEnemyBossTs = System.currentTimeMillis()
                }
            } else if (blockEliteEnemy == null && hitSMapEnemyOrange.containsSimilarColor(color)) {
                val data = Block("enemySMapO", x, y, 10.pt, 16.pt)
                if (Analysis.hit(ss, data, hitSMapEnemyOrange, 3.pt, 0.3f)) {
                    blockEliteEnemy = data
                    lastEliteEnemyTs = System.currentTimeMillis()
                }
            } else if (blockEliteEnemy == null && hitSMapEnemyYellow.containsSimilarColor(color)) {
                val data = Block("enemySMapY", x, y, 10.pt, 16.pt)
                if (Analysis.hit(ss, data, hitSMapEnemyYellow, 3.pt, 0.3f)) {
                    blockEliteEnemy = data
                    lastEliteEnemyTs = System.currentTimeMillis()
                }
            } else if (blockEliteEnemy == null && hitSMapEnemyBlue.containsSimilarColor(color)) {
                val data = Block("enemySMapB", x, y, 10.pt, 16.pt)
                if (Analysis.hit(ss, data, hitSMapEnemyBlue, 3.pt, 0.3f)) {
                    blockEliteEnemy = data
                    lastEliteEnemyTs = System.currentTimeMillis()
                }
            }
            foreach(x, y)
            y += sp
        }
        x -= sp
    }
}

var sMapArray: Array<IntArray>? = null
fun analysisRasterizeMap(returnMapArray: Array<IntArray>): Int? {
    val startPoint = (selfInSmallMap.x - smallMap.left to selfInSmallMap.y - smallMap.top)
    val notArrivedBfsPath: PathNodeAStar? =
        takeTargetAStarPath(returnMapArray, startPoint, mapTargetList)
    if (isDebug) {
        var drawEndPath = notArrivedBfsPath
        while(drawEndPath != null) {
            returnMapArray[drawEndPath.x][ drawEndPath.y] = PLAN
            drawEndPath = drawEndPath.previous
        }
        sMapArray = returnMapArray // test mode
    }
    if (notArrivedBfsPath != null) lastBfsPath = notArrivedBfsPath
    return notArrivedBfsPath?.averageMoveAngle
}

val emptyPrimaryRoad by lazy { emptySet<ColorDiffer>() }
val mapArray by lazy {
    Array(smallMap.width) { IntArray(smallMap.height) }
}
val searchArray by lazy {
    Array(smallMap.width) { IntArray(smallMap.height) }
}

val mapTopNotArrived = mutableListOf<Pair<Int, Int>>()
val mapRightNotArrived = mutableListOf<Pair<Int, Int>>()
val mapBottomNotArrived = mutableListOf<Pair<Int, Int>>()
val mapLeftNotArrived = mutableListOf<Pair<Int, Int>>()
val mapNotArrived = mutableListOf<Pair<Int, Int>>()
val mapSampleArrived = mutableListOf<Pair<Int, Int>>()
//private val mapTopNotArrivedB = mutableListOf<Pair<Int, Int>>() // 2级
//private val mapRightNotArrivedB = mutableListOf<Pair<Int, Int>>() // 2级
//private val mapBottomNotArrivedB = mutableListOf<Pair<Int, Int>>() // 2级
//private val mapLeftNotArrivedB = mutableListOf<Pair<Int, Int>>() // 2级

var mapSampleCounter = 0
val mapBorderList = mutableListOf<Pair<Int, Int>>()
val mapTargetList = mutableListOf<Pair<Int, Int>>()
val mapEnemyList =  mutableListOf<Pair<Int, Int>>()
val mapEnemyMYBossList = mutableListOf<Pair<Int, Int>>()
val mapBlacksmithList = mutableListOf<Pair<Int, Int>>()
val mapBlueDoorList = mutableListOf<Pair<Int, Int>>()
val mapRedDoorList = mutableListOf<Pair<Int, Int>>()
val mapPrimaryRoadList = mutableListOf<Pair<Int, Int>>()
val mapNpcList = mutableListOf<Pair<Int, Int>>()   // npc
val mapRedDotList = mutableListOf<Pair<Int, Int>>() // 小怪
val mapEquipOList =  mutableListOf<Pair<Int, Int>>()
val mapBoxList = mutableListOf<Pair<Int, Int>>()
var lastMapTopC: Int = 0
var lastMapBottomC: Int = 0
var lastMapLeftC: Int = 0
var lastMapRightC: Int = 0
var lastMapTopP: Float? = null
var lastMapBottomP: Float? = null
var lastMapLeftP: Float? = null
var lastMapRightP: Float? = null
//var lastMapTopPb: Float? = null
//var lastMapBottomPb: Float? = null
//var lastMapLeftPb: Float? = null
//var lastMapRightPb: Float? = null
var lastMapRoadC: Int = 0
var lastMapTopNotArrivedTs = 0L
var lastMapRightNotArrivedTs = 0L
var lastMapBottomNotArrivedTs = 0L
var lastMapLeftNotArrivedTs = 0L
var lastMapRedDotTs = 0L


fun rasterizeMap(ss: Bitmap,
                 hitRoad: Set<ColorDiffer>): Array<IntArray> {
    return rasterizeMap(ss, hitRoad, emptyPrimaryRoad, filterEnemyY = false, filterRedDot = false)
}
fun rasterizeMap(ss: Bitmap,
                 hitRoad: Set<ColorDiffer>,
                 hitPrimaryRoad: Set<ColorDiffer>,
                 filterEnemyY: Boolean,
                 filterRedDot: Boolean): Array<IntArray> {
    val returnMapArray = takeUnsetMapArray(ss, filterRedDot = filterRedDot)

    val waitAnalysisPoints = LinkedList<Pair<Int, Int>>() // x. y. color
    waitAnalysisPoints.add(Pair(selfInSmallMap.x - smallMap.left, selfInSmallMap.y - smallMap.top))
    while (waitAnalysisPoints.isNotEmpty()) {
        val point = waitAnalysisPoints.removeAt(0)
        val x = point.first
        val y = point.second
        if (x < 0 || x >= returnMapArray.size || y < 0 || y >= returnMapArray[0].size) {
            continue
        }
        if (searchArray[x][y] == 1) {
            continue
        }
        searchArray[x][y] = 1
        if (returnMapArray[x][y] == ROAD || returnMapArray[x][y] == SELF
            || returnMapArray[x][y] == ENEMY
            || returnMapArray[x][y] == BLACKSMITH
            || returnMapArray[x][y] == BLUE_DOOR
            || returnMapArray[x][y] == RED_DOOR
            || returnMapArray[x][y] == NPC
            || returnMapArray[x][y] == RED_DOT
            || returnMapArray[x][y] == EQUIP) {
            if (y - step >= step) waitAnalysisPoints.add(Pair(x, y - step))
            if (x + step < returnMapArray.size - step) waitAnalysisPoints.add(Pair(x + step, y))
            if (y + step < returnMapArray[x].size - step) waitAnalysisPoints.add(Pair(x, y + step))
            if (x - step >= step) waitAnalysisPoints.add(Pair(x - step, y))
            if (returnMapArray[x][y] == ENEMY) {
                mapEnemyList.add(x to y)
            } else if (returnMapArray[x][y] == BLACKSMITH) {
                mapBlacksmithList.add(x to y)
            } else if (returnMapArray[x][y] == BLUE_DOOR) {
                mapBlueDoorList.add(x to y)
            } else if (returnMapArray[x][y] == RED_DOOR) {
                mapRedDoorList.add(x to y)
            } else if (returnMapArray[x][y] == NPC) {
                mapNpcList.add(x to y)
            } else if (!filterRedDot && returnMapArray[x][y] == RED_DOT) {
                mapRedDotList.add(x to y)
            } else if (returnMapArray[x][y] == EQUIP) {
                mapEquipOList.add(x to y)
            }
        } else {
            val c = App.take().takePixel(ss, x + smallMap.left, y + smallMap.top)
            if (hitRoad.containsSimilarColor(c)) {
                returnMapArray[x][y] = ROAD
                pointDiffusion(waitAnalysisPoints, x, y, returnMapArray)
                if (hitPrimaryRoad.containsSimilarColor(c)) {
                    mapPrimaryRoadList.add(x to y)
                }
            } else if (hitSMapEnemyPurple.containsSimilarColor(c)
                || hitSMapEnemyBlue.containsSimilarColor(c)
                || (!filterEnemyY && hitSMapEnemyYellow.containsSimilarColor(c))
                || hitSMapEnemyOrange.containsSimilarColor(c)) {
                returnMapArray[x][y] = ENEMY
                mapEnemyList.add(x to y)
                pointDiffusion(waitAnalysisPoints, x, y, returnMapArray)
            } else {
                returnMapArray[x][y] = BORDER
                if ((y <= selfInSmallMap.top && y >= selfInSmallMap.top - step) ||
                    (y >= selfInSmallMap.bottom && y <= selfInSmallMap.bottom + step) ||
                    (x <= selfInSmallMap.left && x >= selfInSmallMap.left - step) ||
                    (x >= selfInSmallMap.right && x <= selfInSmallMap.right + step)) {
                    mapBorderList.add(x to y)
                }
            }
        }
    }

    if (mapTopNotArrived.size > 0) lastMapTopNotArrivedTs = System.currentTimeMillis()
    if (mapRightNotArrived.size > 0) lastMapRightNotArrivedTs = System.currentTimeMillis()
    if (mapBottomNotArrived.size > 0) lastMapBottomNotArrivedTs = System.currentTimeMillis()
    if (mapLeftNotArrived.size > 0) lastMapLeftNotArrivedTs = System.currentTimeMillis()
    if (mapRedDotList.size > 0) lastMapRedDotTs = System.currentTimeMillis()

    lastMapTopP = if (mapTopNotArrived.isNotEmpty()) mapTopNotArrived.map { it.first }.average().toFloat() / smallMap.width.toFloat() else null
    lastMapBottomP = if (mapBottomNotArrived.isNotEmpty()) mapBottomNotArrived.map { it.first }.average().toFloat() / smallMap.width.toFloat() else null
    lastMapLeftP = if (mapLeftNotArrived.isNotEmpty()) mapLeftNotArrived.map { it.second }.average().toFloat() / smallMap.height.toFloat() else null
    lastMapRightP = if (mapRightNotArrived.isNotEmpty()) mapRightNotArrived.map { it.second }.average().toFloat() / smallMap.height.toFloat() else null
    lastMapTopC = mapTopNotArrived.size
    lastMapBottomC = mapBottomNotArrived.size
    lastMapLeftC = mapLeftNotArrived.size
    lastMapRightC = mapRightNotArrived.size
    lastMapRoadC = mapSampleArrived.size

    return returnMapArray
}

// 遍历点扩散
private fun pointDiffusion(waitAnalysisPoints: LinkedList<Pair<Int, Int>>, x: Int, y: Int, returnMapArray: Array<IntArray>) {
    if (y - step >= step) waitAnalysisPoints.add(Pair(x, y - step))
    if (x + step < returnMapArray.size - step) waitAnalysisPoints.add(Pair(x + step, y))
    if (y + step < returnMapArray[x].size - step) waitAnalysisPoints.add(Pair(x, y + step))
    if (x - step >= step) waitAnalysisPoints.add(Pair(x - step, y))
    if (y <= step * 3) {
        mapTopNotArrived.add(x to y)
    } else if (x >= returnMapArray.size - step * 3) {
        mapRightNotArrived.add(x to y)
    } else if (y >= returnMapArray[x].size - step * 3) {
        mapBottomNotArrived.add(x to y)
    } else if (x <= step * 3) {
        mapLeftNotArrived.add(x to y)
    } else {
        if (mapSampleCounter == 100000) mapSampleCounter = 0
        mapSampleCounter++
        if (mapSampleCounter % 16 == 0) {
            mapSampleArrived.add(x to y)
        }
    }

}

fun takeUnsetMapArray(ss: Bitmap, filterRedDot: Boolean = false): Array<IntArray> {
//    printLog {
//        val sb = StringBuilder()
//        sb.append("analystMap info [")
//        if (mapEnemyList.size > 0) sb.append("enemy${mapEnemyList.size}#")
//        if (mapBlacksmithList.size > 0) sb.append("铁匠${mapBlacksmithList.size}#")
//        if (mapBlueDoorList.size > 0) sb.append("传送门${mapBlueDoorList.size}#")
//        if (mapRedDoorList.size > 0) sb.append("红门${mapRedDoorList.size}#")
//        if (mapNpcList.size > 0) sb.append("npc${mapNpcList.size}#")
//        if (mapRedDotList.size > 0) sb.append("红点${mapRedDotList.size}#")
//        sb.append("]")
//        sb.toString()
//    }

    val returnMapArray = mapArray
    val searchFlag = searchArray
    // 重置
    var a = 0
    while (a < returnMapArray.size) {
        var b = 0
        while (b < returnMapArray[0].size) {
            returnMapArray[a][b] = NONE
            searchFlag[a][b] = NONE
            b++
        }
        a++
    }

    mapTopNotArrived.clear()
    mapRightNotArrived.clear()
    mapBottomNotArrived.clear()
    mapLeftNotArrived.clear()
    mapBorderList.clear()
    mapEnemyList.clear()
    mapEnemyMYBossList.clear()
    mapBlacksmithList.clear()
    mapBlueDoorList.clear()
    mapRedDoorList.clear()
    mapNpcList.clear()
    mapRedDotList.clear()
    mapBoxList.clear()
    mapEquipOList.clear()
    mapPrimaryRoadList.clear()
    mapSampleArrived.clear()
    mapTargetList.clear()

    val s = 1
    var pX = selfInSmallMap.x - smallMap.left - step
    while(pX > step) pX -= step // 初始化x 单位是0开始的
    var pY = selfInSmallMap.y - smallMap.top - step
    while(pY > step) pY -= step // 初始化y 单位是0开始的
    val sY = pY
    while (pX < smallMap.width) {
        pY = sY
        while (pY < smallMap.height) {
            val color = App.take().takePixel(ss, pX + smallMap.left, pY + smallMap.top)
            if (pX >= selfInSmallMap.left - smallMap.left - step * s
                && pX < selfInSmallMap.right - smallMap.left + step * s
                && pY >= selfInSmallMap.top - smallMap.top - step * s
                && pY < selfInSmallMap.bottom - smallMap.top + step * s
                && hitSelf.containsSimilarColor(color)) {
                setupElementsByBlock(pX to pY, SELF, returnMapArray) //扩散1格
            } else if (pX >= selfInSmallMap.x - smallMap.left - step
                && pX < selfInSmallMap.x - smallMap.left + step
                && pY >= selfInSmallMap.y - smallMap.top - step
                && pY < selfInSmallMap.y - smallMap.top + step) {
                setupElementsByBlock(pX to pY, SELF, returnMapArray)
            } else if (pX >= selfInSmallMap.x - smallMap.left - step
                && pX < selfInSmallMap.x - smallMap.left + step
                && pY >= selfInSmallMap.y - smallMap.top - step
                && pY < selfInSmallMap.y - smallMap.top + step) {
                setupElementsByBlock(pX to pY, SELF, returnMapArray)
            } else if (hitSMapEnemyPurple.containsSimilarColor(color)
                || hitSMapEnemyBlue.containsSimilarColor(color)
                || hitSMapEnemyYellow.containsSimilarColor(color)
                || hitSMapEnemyOrange.containsSimilarColor(color)) {
                // 蓝色看成紫色
                setupElementsByBlock(pX to pY, ENEMY, returnMapArray)
            } else if (hitBlacksmith.containsSimilarColor(color)) {
                setupElementsByBlock(pX to pY, BLACKSMITH, returnMapArray)
            } else if (hitBlueDoor.containsSimilarColor(color)) {
                setupElementsByBlock(pX to pY, BLUE_DOOR, returnMapArray)
            } else if (hitRedDoor.containsSimilarColor(color)) {
                setupElementsByBlock(pX to pY, RED_DOOR, returnMapArray)
            } else if (hitSMapNpc.containsSimilarColor(color)) {
                setupElementsByBlock(pX to pY, NPC, returnMapArray)
            } else if (hitEquipO.containsSimilarColor(color)) {
                setupElementsByBlock(pX to pY, EQUIP, returnMapArray)
            } else if (!filterRedDot && hitSMapEnemyRedDot.containsSimilarColor(color)) {
                setupElementsByBlock(pX to pY, RED_DOT, returnMapArray)
            }

            pY += step
        }
        pX += step
    }
    return returnMapArray
}

val mapSimpleStatus: String? get() {
    if (!isDebug) return null
    val sb = StringBuilder()
    sb.append("[上,${(System.currentTimeMillis() - lastMapTopNotArrivedTs) / 1000}s")
    lastMapTopP?.also { sb.append(",${it}") }
    sb.append(",c:${lastMapTopC}]")
    sb.append("[下,${(System.currentTimeMillis() - lastMapBottomNotArrivedTs) / 1000}s")
    if (lastMapBottomP != null) sb.append(",${lastMapBottomP}")
    sb.append(",c:${lastMapBottomC}]")
    sb.append("[左,${(System.currentTimeMillis() - lastMapLeftNotArrivedTs) / 1000}s")
    if (lastMapLeftP != null) sb.append(",${lastMapLeftP}")
    sb.append(",c:${lastMapLeftC}]")
    sb.append("[右,${(System.currentTimeMillis() - lastMapRightNotArrivedTs) / 1000}s")
    if (lastMapRightP != null) sb.append(",${lastMapRightP}")
    sb.append(",c:${lastMapRightC}]")
    sb.append("]")
    sb.append("[Road,c:${lastMapRoadC}]")
    return sb.toString()
}

fun mapElementDistance(target: Pair<Int, Int>): Int {
    val offsetX = target.first - (selfInSmallMap.x - smallMap.left)
    val offsetY = target.second - (selfInSmallMap.y - smallMap.top)
    return sqrt((offsetX * offsetX + offsetY * offsetY).toDouble()).toInt()
}
fun mapElementDistance(block: Block?): Int {
    if (block == null) return Int.MAX_VALUE
    val offsetX = block.x - (selfInSmallMap.x - smallMap.left)
    val offsetY = block.y - (selfInSmallMap.y - smallMap.top)
    return sqrt((offsetX * offsetX + offsetY * offsetY).toDouble()).toInt()
}
fun mapElementAngle(block: Block?): Int? {
    if (block == null) return null
    val offsetX = block.x - selfInSmallMap.x
    val offsetY = block.y - selfInSmallMap.y
    return App.take().pointToAngle(offsetX, offsetY)
}